
/*
 *
 *       Filename:  HttpRequest.cc
 *
 *    Description:
 *
 *        Version:  1.0
 *        Created:  03/05/19 11:04:03
 *       Revision:  none
 *       Compiler:  gcc
 *
 *         Author:  liushuyong (), shuyong1999@163.com
 *        Company:  shuyong
 *
 * =====================================================================================
 */
#include "RequestObj.h"
#include "SessionCache.h"
#include "TTSServiceImpl.h"
#include "HttpAck.h"
#include "CommFunc.h"

#include "Statistics.h"

DECLARE_int32(max_text_len);    //最大文本长度
DECLARE_bool(use_repair_tools); //使用修复工具
DECLARE_bool(isDebugInfo);

RequestObj::RequestObj() {
#ifdef Debug
    //    LOG(INFO) << "New" << endl;
#endif
}

RequestObj::~RequestObj() {
#ifdef Debug
    //    LOG(INFO) << "Del" << endl;
#endif
}

/**
 * @brief  处理合成请求
 */
void RequestObj::handleRequest() {
    int ret = 0;
    bool isFromCache = false;
    int errNo;     // getErrNo
    errNo = OK;
    HttpParamSPtr spHttpParam = mspRPCContext->mspHttpParam;
    //从Session 缓存中获取Session
    bool isNew = false;
    RequestSessionSPtr spSession = SessionCache::getInstance()->attachSession(spHttpParam, errNo, isNew);  //

    if (spSession == NULL) {
        LOG(ERROR) << "session null" << endl;
        httpAck(mspRPCContext, errNo, 0.0, "0", "");
        return;
    }

    //初始化Session TODO 更新时间
    //设置session的参数

    if (isNew) {   //此session的第一个包
        std::wstring wTxt = S2WS(spHttpParam->mTxt);
        int wTxtLen = wTxt.length();    // 正好与java的编码及长度计算相吻合,即返回的是字符数， 不是字节数

        if (wTxtLen <= 0) {
            LOG(ERROR) << "txt too short" << endl;
            errNo = ERROR_TEXT_TOO_SHORT;
        } else if (wTxtLen > FLAGS_max_text_len) {
            LOG(ERROR) << "len=" << spHttpParam->mTxt.length() << ", txt too long" << endl;
            errNo = ERROR_TEXT_TOO_LONG;
        }

        if (errNo != OK) {
            httpAck(mspRPCContext, errNo, 0.0, "0", "");
            return;
        }

        if (FLAGS_isDebugInfo) {
            spSession->mDebugInfo.setInfo("text", spHttpParam->mTxt);

            for (int i = 0; i < mRuleSrcVector.size(); i++) {
                spSession->mDebugInfo.setInfo("rule_src" + std::to_string(i + 1), mRuleSrcVector[i]);
                spSession->mDebugInfo.setInfo("rule_dest" + std::to_string(i + 1), mRuleDestVector[i]);
            }
        }

        spSession->doSynthetize(mspRPCContext); //不能再其后操作HttpParam等对象的操作，因为其内内被部会被释放, 在退出函数时use_count -1, 只留下zone引用
    } else {
        do {
            if (spHttpParam->mSeqID < 0) {  //session存在, 但是用户发过来一个seq < 0的请求表明用户想结束当前的txt的合成
                LOG(INFO) << "req=" << spHttpParam->mReqID << ", seq=" << spHttpParam->mSeqID << ":sess stop" << endl;
                SessionCache::getInstance()->delSession(spHttpParam->mReqID);
                // TODO 需要应答吗？
                errNo = ERROR_STOP;
                httpAck(mspRPCContext, errNo, 0.0, "0", "");
                break;
            }

            //LOG(INFO) << "req=" << spHttpParam->mReqID << ", seq=" << spHttpParam->mSeqID << ":read cache" << endl;
            //同步读取cache
            spSession->readCache(mspRPCContext);
        } while (0);
    }
}

/**
 * @brief 设置当前请求的剩下文
 *
 * @param cntl
 * @param done
 */
void RequestObj::newContxtInfo(Controller* cntl, Closure* done) {
    mspRPCContext = make_shared < RPCContext > ();
    mspRPCContext->mpRpcCntl = cntl;
    mspRPCContext->mpRpcDone = done;
}

/**
 * @brief 解析请求的数据
 *
 * @return
 */
int RequestObj::parseRequest() {
    int ret = -1;
    string* reqIDVal;
    string* seqIDVal;
    string* propertyVal;
    string* appIDVal;
    int errNo = -1; // getErrNo
    //string* protoVal;
    //string* serviceTypeVal;
    //string* userIDVal;
    //string* netStateVal;
    HttpParamSPtr spHttpParam = mspRPCContext->mspHttpParam;

    do {
        if (NULL == mspRPCContext->mpRpcCntl) {
            LOG(ERROR) << "BrpcCtrl nul" << endl;
            errNo = ERROR_REQUEST_CNTL;
            break;
        }

        seqIDVal = const_cast < string* >(mspRPCContext->mpRpcCntl->http_request().GetHeader("Sequence-Id"));

        if (seqIDVal == NULL || seqIDVal->empty()) {
            LOG(ERROR) << "seq nul" << endl;
            errNo = ERROR_SEQID_NULL;
            break;
        }

        try {
            spHttpParam->mSeqID = stoi(*seqIDVal);
        } catch (std::exception& e) {
            LOG(ERROR) << "seq err" << endl;
            errNo = ERROR_SEQID_NULL;
            break;
        }

        reqIDVal = const_cast < string* >(mspRPCContext->mpRpcCntl->http_request().GetHeader("Request-Id"));

        if (reqIDVal == NULL || reqIDVal->empty()) {
            errNo = ERROR_REQID_NULL;
            LOG(ERROR) << "req nul" << endl;
            break;
        }

        spHttpParam->mReqID = *reqIDVal;

        if (spHttpParam->mSeqID <= 1) { //只在第一包的时候， 解析详细的数据
            appIDVal = const_cast < string* >(mspRPCContext->mpRpcCntl->http_request().GetHeader("Application-Id"));
            propertyVal = const_cast < string* >(mspRPCContext->mpRpcCntl->http_request().GetHeader("Property"));
            string text = mspRPCContext->mpRpcCntl->request_attachment().to_string();
            Statistics::addTotalReqCount(); //请求次数统计

            if (appIDVal == NULL || appIDVal->empty()) {
                LOG(ERROR) << "appid NULL" << endl; //appkey 是平台定义的字段， appid是TTS开发定义的字段
                //break;              //走网关就不需要了appid的字段了， 所以不再为空是认为是错误的
                spHttpParam->mAppID = "";
            } else {
                spHttpParam->mAppID = *appIDVal;
            }

            if (propertyVal == NULL || propertyVal->empty()) {
                LOG(ERROR) << "Property nul" << endl;
                errNo = ERROR_PROPERTY_NULL;
                break;
            }

            ret = parseProperty(propertyVal);

            if (ret != 0) {
                errNo = ERROR_PROPERTY_NULL;
                LOG(ERROR) << "Property Err" << endl;
                break;
            }

            if (text.length() == 0) {   //建议只有在第一包的时候发送文本内容,后边的包发动为空， 加快传输速度
                spHttpParam->mTxt = "";
            } else {
                spHttpParam->mTxt = text;
            }

            //检查是否是SSML
            if (spHttpParam->mEngineParam[TEXT_TYPE_INDEX] == 1) {
                vector < string > ruleSrcVector, ruleDestVector;
                int tim = spHttpParam->mEngineParam[TIMBRE_INDEX];  //音色
                LOG(INFO) << "newTxt=" << spHttpParam->mTxt << endl;

                if (ruleSrcVector.size() > 0) {
                    mRuleSrcVector = ruleSrcVector;
                    mRuleDestVector = ruleDestVector;

                    for (int i = 0; i < ruleSrcVector.size(); i++) {
                        LOG(INFO) << "src:" << ruleSrcVector[i] << ", dest:" << ruleDestVector[i] << endl;
                    }
                } else {
                    LOG(ERROR) << "srcRule=0" << endl;
                }
            }

            LOG(INFO) << "req=" << spHttpParam->mReqID << ", seq=" << spHttpParam->mSeqID << ":txt=" << spHttpParam->mTxt << endl;
        } else {   //第二包以后只解析， seq和req
            //LOG(INFO) << "req=" << spHttpParam->mReqID << ", seq=" << spHttpParam->mSeqID << endl;
        }

        ret = 0;
    } while (0);

    //应答出错
    if (0 != ret) {
        httpAck(mspRPCContext, errNo, 0.0, "0", "");
    }

    return ret;
}

/**
 * @brief
 * @TODO  可以优化为只在第一包的时候解析
 * @param propertyVal
 *
 * @return
 */
int RequestObj::parseProperty(const string* propertyVal) {
    int ret = 0;
    char* property = const_cast < char*>((*propertyVal).c_str());
    cJSON* pRoot = NULL;
    cJSON* pItem = NULL;
    cJSON* tmp = NULL;
    double value = 0.0;
    string* isDebugVal;
    HttpParamSPtr spHttpParam = mspRPCContext->mspHttpParam;
    pRoot = cJSON_Parse(property);

    if (NULL == pRoot) {
        LOG(ERROR) << " cJSON_Parse failed" << endl;
        return -1;
    }

    do {
        //isDebug  是否要返回debug信息
        pItem = cJSON_GetObjectItem(pRoot, "debug");

        if (pItem && pItem->valuestring != NULL) {
            spHttpParam->mIsDebug = (std::string) pItem->valuestring;
        } else {
            spHttpParam->mIsDebug = "0";
        }

        //timestamp  删掉了
        //TTS合成参数
        pItem = cJSON_GetObjectItem(pRoot, "parameters");

        if (!pItem) {
            LOG(ERROR) << "param nul" << endl;
            ret = -1;
            break;
        }

        for (int i = 0; i < SUPPORT_PARAMETERS_NUM; ++i) {
            if ((tmp = cJSON_GetObjectItem(pItem, ENGINE_PARAMETERS_KEY[i])) != NULL) { //安装默认的顺去取值
                if (i == VOLUME_INDEX || i == SPEED_INDEX) {
                    if (tmp->type == cJSON_Number) {
                        value = tmp->valuedouble;
                    } else if (tmp->type == cJSON_String) {
                        value = atof(tmp->valuestring);
                    } else {
                        LOG(ERROR) << "format err" << endl;
                        ret = -1;
                        break;
                    }
                } else {
                    if (tmp->type == cJSON_Number) {
                        value = tmp->valueint;
                    } else if (tmp->type == cJSON_String) {
                        value = atoi(tmp->valuestring);
                    } else {
                        LOG(ERROR) << "format err" << endl;
                        ret = -1;
                        break;
                    }
                }

                if (value < ENGINE_PARAMETERS_MIN[i] || value > ENGINE_PARAMETERS_MAX[i]) {
                    LOG(ERROR) << "key:" << ENGINE_PARAMETERS_KEY[i] << ", val=" << value << ",err" << endl;
                    ret = -2;
                    break;
                }

                spHttpParam->mEngineParam[i] = value;
            }
        }

        //break while
        if (ret != 0) {
            break;
        }
    } while (0);

    if (pRoot) {
        cJSON_Delete(pRoot);
    }

    return ret;
}
